#include <future>

#include "atomic_incr.pb.h"
#include "netlib/base/repl.h"
#include "netlib/net/event/event_loop_thread.h"
#include "netlib/rpc/rpc_client.h"

namespace netlib::examples {

class AtomicIncreaseClient : NonCopyable {
public:
	explicit AtomicIncreaseClient(net::EventLoop* loop,
	                              const net::InetAddress& addr,
	                              std::string&& name = "AtomicIncreaseClient") :
	    client_(loop, addr, std::move(name)) {
		repl_.AddCommand("increase", [this](char* args) { return IncreaseCommand(args); });
		repl_.AddCommand("setget", [this](char* args) { return SetGetCommand(args); });
	}

	void Connect() { client_.Connect(); }
	bool IsConnected() const { return client_.IsConnected(); }
	void Run() { repl_.Run(); }

private:
	int IncreaseCommand(char* args) {
		char* end{nullptr};
		auto num = std::strtol(args, &end, 10);
		assert(*end == '\0');

		AtomicIncreaseService::Stub stub(client_.GetRpcChannel());
		AtomicIncreaseRequest request;
		request.set_num(num);
		AtomicIncreaseResponse response;
		std::promise<void> promise;
		auto future = promise.get_future();
		auto done =
		    google::protobuf::NewCallback(&AtomicIncreaseClient::DoneCallback, &response, &promise);
		stub.Increase(nullptr, &request, &response, done);
		future.wait();
		return 0;
	}
	int SetGetCommand(char* args) {
		char* end{nullptr};
		auto num = std::strtol(args, &end, 10);
		assert(*end == '\0');

		AtomicIncreaseService::Stub stub(client_.GetRpcChannel());
		AtomicIncreaseRequest request;
		request.set_num(num);
		auto response = new AtomicIncreaseResponse;
		std::promise<void> promise;
		auto future = promise.get_future();
		auto done =
		    google::protobuf::NewCallback(&AtomicIncreaseClient::DoneCallback, response, &promise);
		stub.SetGet(nullptr, &request, response, done);
		future.wait();
		return 0;
	}
	static void DoneCallback(AtomicIncreaseResponse* resq, std::promise<void>* promise) {
		std::cout << "      Result: " << resq->result() << std::endl; // NOLINT
		promise->set_value();
	}

private:
	REPL repl_;
	rpc::RpcClient client_;
};

} // namespace netlib::examples

int main() {
	using namespace netlib;
	net::InetAddress addr(23456);
	net::EventLoopThread loop_thread;
	examples::AtomicIncreaseClient client(loop_thread.StartLoop(), addr);
	client.Connect();
	if (!client.IsConnected()) {
		return 1;
	}
	client.Run();
	return 0;
}